package com.merry.beanFactory.ac;

import java.util.function.Supplier;

import org.springframework.beans.factory.config.BeanDefinitionCustomizer;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.*;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

/**
 * 回到了比较重要的类：在SpringBoot中，如果我们启动了一个SpringBoot的项目，但是这个项目里面没有 web容器，那么就会启动一个这个类。
 *      1.实现了：GenericApplicationContext类。
 *      2.实现了：AnnotationConfigRegistry接口，表示，这个容器具有扫描 注解的能力。
 */
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

    /**
     * 扫描注解的，并将注解的bean，变成 BeanDefinition 对象，注册进容器。
     */
    private final AnnotatedBeanDefinitionReader reader;

    /**
     * 扫描 类路径的，与上面的类作用类似。
     */
    private final ClassPathBeanDefinitionScanner scanner;

    /**
     * 这上面两个类都是一个类似于 “BeanDefinitionReader”。的结构，里面内置了一个 BeanDefinitionRegistry。
     * 可以实现，当阅读器，读到需要被注册的bean时，由注册器完成注册。
     */


    /**
     * 构造器，并且初始化上面的两个阅读器，将自己（也是一个beanDefinition注册器），注入阅读器。
     */
    public AnnotationConfigApplicationContext() {
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
        super(beanFactory);
        this.reader = new AnnotatedBeanDefinitionReader(this);
        this.scanner = new ClassPathBeanDefinitionScanner(this);
    }

    /**
     * 注册一个或者多个 注解的 class对象，比如：如果容器添加了 {@see Component}、{@see Service}、{@see Configuration}、
     *  类，那么在该容器启动的时候，这个阅读器只会找，有上面三个注解修饰的 类。
     */
    public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
        this();
        register(componentClasses);
        refresh();
    }

    /**
     * 配置，需要扫描注解的 包路径。
     */
    public AnnotationConfigApplicationContext(String... basePackages) {
        this();
        scan(basePackages);
        refresh();
    }


    /**
     * 给两个阅读器，注册一个环境。
     * {@link AnnotatedBeanDefinitionReader} and {@link ClassPathBeanDefinitionScanner}.
     */
    @Override
    public void setEnvironment(ConfigurableEnvironment environment) {
        super.setEnvironment(environment);
        this.reader.setEnvironment(environment);
        this.scanner.setEnvironment(environment);
    }

    /**
     * 给这两个阅读器设置一个 BeanName 生成器。
     */
    public void setBeanNameGenerator(BeanNameGenerator beanNameGenerator) {
        this.reader.setBeanNameGenerator(beanNameGenerator);
        this.scanner.setBeanNameGenerator(beanNameGenerator);
        getBeanFactory().registerSingleton(
                AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
    }

    /**
     * 给两个阅读器设置 ScopeMetadataResolver
     */
    public void setScopeMetadataResolver(ScopeMetadataResolver scopeMetadataResolver) {
        this.reader.setScopeMetadataResolver(scopeMetadataResolver);
        this.scanner.setScopeMetadataResolver(scopeMetadataResolver);
    }


    //---------------------------------------------------------------------
    // 实现 AnnotationConfigRegistry 接口的方法。
    //---------------------------------------------------------------------

    /**
     * 注册一个或者多个 component类。
     */
    @Override
    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        this.reader.register(componentClasses);
    }

    /**
     * 扫描这个包下面的所有子类，并注册进入容器，如果需要的话。
     */
    @Override
    public void scan(String... basePackages) {
        Assert.notEmpty(basePackages, "At least one base package must be specified");
        this.scanner.scan(basePackages);
    }


    //---------------------------------------------------------------------
    // Adapt superclass registerBean calls to AnnotatedBeanDefinitionReader
    //---------------------------------------------------------------------

    @Override
    public <T> void registerBean(@Nullable String beanName, Class<T> beanClass,
                                 @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers) {

        this.reader.registerBean(beanClass, beanName, supplier, customizers);
    }

}
